#include "BigNumber.h"

#include <cassert>
#include <cmath>

namespace GiovaMath
{

/*BigNumber::BigNumber(UInt32 pageSize)
: mIndex(0),
mPageSize(pageSize),
mLenght(0),
mData(NULL)
{
	assert(pageSize > 0);
	_allocMemory();
}*/

BigNumber::BigNumber(UInt64 number, UInt32 pageSize)
:
mIndex(0),
mPageSize(pageSize),
mLenght(0),
mData(NULL)
{
	assert(pageSize > 0);

	do 
	{
		if(mIndex + 1 > mLenght)
			_allocMemory();

		mData[mIndex] = number % 10;
		number /= 10;

	} while (number > 0 && ++mIndex);
}

BigNumber::BigNumber(const char* sNumber, UInt32 pageSize)
: 
mIndex(0),
mPageSize(pageSize),
mLenght(0),
mData(NULL)
{
	assert(pageSize > 0 && sNumber != NULL);

	UInt32 string_len = strlen(sNumber);

	_allocMemory(string_len);

	for (Int32 i = string_len - 1; i >= 0; --i)
	{
		mData[string_len - i - 1] = sNumber[i] - 48;
	}

	mIndex = string_len - 1;
}

BigNumber::BigNumber(const BigNumber &bNumber)
: 
mIndex(bNumber.mIndex),
mPageSize(bNumber.mPageSize),
mLenght(bNumber.mLenght),
mData(NULL)
{
	mData = static_cast<Byte*>(malloc(mLenght * sizeof(Byte)));
	memcpy(mData, bNumber.mData, bNumber.mLenght);
}

BigNumber::~BigNumber()
{
	free(mData);
	mData = NULL;
}

BigNumber& BigNumber::operator= (const BigNumber& bNumber)
{
	if(this != &bNumber) // avoid self assignment
	{
		if(bNumber.mLenght > mLenght)
			_allocMemory(bNumber.mLenght - mLenght);

		mIndex = bNumber.mIndex;

		memcpy(mData, bNumber.mData, bNumber.mLenght);		

	}

	return *this;
}

BigNumber& BigNumber::operator-= (const BigNumber& bNumber)
{
	 bNumber;
	 return *this; // TODO
}

BigNumber& BigNumber::operator-= (UInt32 number)
{
	number;
	return *this; // TODO
}

BigNumber& BigNumber::operator+= (const BigNumber& bNumber)
{
	UInt32 maxIndex = (mIndex > bNumber.mIndex) ? mIndex : bNumber.mIndex;

	while(bNumber.mIndex >= mLenght)
	{
		_allocMemory();
	}

	Byte rest = 0;
	Byte sum = 0;
	UInt i = 0;

	do
	{
		sum =  (i <= bNumber.mIndex) ? bNumber.mData[i] : 0;
		sum += mData[i] + rest;

		mData[i] = (sum > 9) ? (sum - 10) : sum;

		rest = (sum > 9) ? 1 : 0;

		if(i >= mLenght - 1)
			_allocMemory();

	}
	while(++i <= maxIndex || rest > 0);

	mIndex = i - 1;

	return *this;
}

BigNumber& BigNumber::operator+= (UInt32 number)
{
	BigNumber bNumber = BigNumber(number);
	*this += bNumber;
	return *this;
}

BigNumber& BigNumber::operator= (UInt32 number)
{
	clearToZero();

	do 
	{
		if(mIndex + 1 > mLenght)
			_allocMemory();

		mData[mIndex] = number % 10;
		number /= 10;

	} while (number > 0 && ++mIndex);

	return *this;
}

BigNumber& BigNumber::operator*= (const BigNumber& bNumber)
{
	assert(bNumber.mIndex <= 19);

	BigNumber temp = *this;
	UInt64 value = bNumber.toInt64();

	while (--value > 0)
	{
		*this += temp;
	}

	return *this;
}

BigNumber& BigNumber::operator*= (UInt32 value)
{
	BigNumber temp = *this;

	while (--value > 0)
	{
		*this += temp;
	}

	return *this;
}

BigNumber& BigNumber::operator/= (const BigNumber& bNumber)
{
	assert(false);
	bNumber;
	return *this; // TODO
}

BigNumber BigNumber::operator- (const BigNumber& bNumber) const
{
	BigNumber bn = *this;

	bn -= bNumber;

	return bn;
}

BigNumber BigNumber::operator+ (const BigNumber& bNumber) const
{
	BigNumber bn = *this;

	bn += bNumber;

	return bn;
}

BigNumber BigNumber::operator* (const BigNumber& bNumber) const
{
	BigNumber bn = *this;

	bn *= bNumber;

	return bn;
}

BigNumber BigNumber::operator/ (const BigNumber& bNumber) const
{
	BigNumber bn = *this;

	bn /= bNumber;

	return bn;
}

BigNumber& BigNumber::operator++ ()
{
	*this += 1;
	return *this;
}

BigNumber BigNumber::operator++ (int)
{
	BigNumber tmp = *this;
	++*this;
	return tmp;
}

BigNumber& BigNumber::operator-- ()
{
	*this -= 1;
	return *this;
}

BigNumber BigNumber::operator-- (int)
{
	BigNumber tmp = *this;
	--*this;
	return tmp;
}

bool BigNumber::operator==(const BigNumber& bNumber) const
{
	if(mIndex != bNumber.mIndex)
		return false;

	for(Int32 i = mIndex; i >= 0; --i)
	{
		if(mData[i] != bNumber.mData[i])
		{
			return false;
		}
	}

	return true;
}

bool BigNumber::operator!=(const BigNumber& bNumber) const
{
	return !(*this == bNumber);
}

bool BigNumber::operator>(const BigNumber& bNumber) const
{
	if(mIndex < bNumber.mIndex)
		return false;

	if(mIndex > bNumber.mIndex)
		return true;

	for(Int32 i = mIndex; i >= 0; --i)
	{
		if(mData[i] != bNumber.mData[i])
		{
			return (mData[i] > bNumber.mData[i]);
		}
	}

	return false;
}

bool BigNumber::operator<(const BigNumber& bNumber) const
{
	if(mIndex > bNumber.mIndex)
		return false;

	if(mIndex < bNumber.mIndex)
		return true;

	for(Int32 i = mIndex; i >= 0; --i)
	{
		if(mData[i] != bNumber.mData[i])
		{
			return (mData[i] < bNumber.mData[i]);
		}
	}

	return false;
}

inline UInt64 BigNumber::toInt64(UInt32 fromDigit) const
{
	assert(fromDigit >= 0 && fromDigit <= mIndex);

	UInt64 value = 0;
	UInt64 exp = 1;

	UInt64 len = (mIndex - fromDigit <= 19) ? mIndex - fromDigit : 19;

	for (UInt64 i = 0; i <= len; ++i)
	{
		exp = 1;
		for(Int64 j = i; j > 0; --j)
			exp *= 10;

		value += mData[i + fromDigit] * exp;
	}

	return value;
}

inline void BigNumber::_allocMemory(UInt32 amount)
{
	assert(amount >= 0);

	amount = (amount == 0) ? mPageSize : amount;

	UInt32 oldLenght = mLenght;

	mLenght += amount;

	mData = static_cast<Byte*>(realloc(mData, mLenght * sizeof(Byte)));

	memset(mData + oldLenght, 0, amount); // clear new memory
}

}